package se.kth.ik2200.carenet_2012;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Properties;

/**
 * Created with IntelliJ IDEA.
 * User: Ruslan Mukhammadov
 * Date: December 3, 2012
 * Time: 1:46 PM
 * To change this template use File | Settings | File Templates.
 */
public class DataTransmissionHandler {

    private static final String DEFAULT_WEB_SERVER_URL = "https://192.16.126.75:8443/sensorData";
    private static String WEB_SERVER_URL;

    public static boolean login(String username, String password)
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        StringBuilder requestParams = new StringBuilder();
        loadRequestParams(requestParams, "username", username);
        loadRequestParams(requestParams, "password", password);

        BufferedReader reader = transmitToWebServer(requestParams);
        String response = reader.readLine();
        boolean userExists = false;
        if (response == null || response.equals("FAIL")) {
            userExists = false;
        } else if (response.equals("OK")) {
            userExists = true;
        }
        reader.close();

        return  userExists;
    }

    /**
     * Sending data to the server through POST method
     *
     * @param data - medical sensor data that is going to be sent
     */
    public static void sendDataToWebServer(SensorData data)
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        StringBuilder requestParams = new StringBuilder();

        loadRequestParams(requestParams, "sensorModel", data.getSensorModel());
        loadRequestParams(requestParams, "sensorDescription", data.getSensorDescription());
        loadRequestParams(requestParams, "sensorPinCode", data.getSensorPinCode());
        loadRequestParams(requestParams, "username", data.getUsername());

        if (data.getSensorModel().equals(NoninOnyx.DEVICE_MODEL)) {
            loadRequestParams(requestParams, "pulseRate", data.getPulseRate());
            loadRequestParams(requestParams, "sp02", data.getSp02());
        } else if (data.getSensorModel().equals(ZephyrBioHarness.DEVICE_MODEL)) {
            loadRequestParams(requestParams, "heartBeat", data.getHeartBeat());
            loadRequestParams(requestParams, "heartRate", data.getHeartRate());
            loadRequestParams(requestParams, "temperature", data.getTemperature());
            loadRequestParams(requestParams, "respiration", data.getRespiration());
            loadRequestParams(requestParams, "posture", data.getPosture());
        }

        Log.printTitle("Uploading sensor data to the Web Server!");

        BufferedReader reader = transmitToWebServer(requestParams);
        reader.close();
    }

    private static void loadRequestParams(StringBuilder requestParams, String key, String value) throws UnsupportedEncodingException {
        requestParams.append(URLEncoder.encode(key, "UTF-8"));
        requestParams.append("=").append(URLEncoder.encode(value, "UTF-8"));
        requestParams.append("&");
    }

    private static BufferedReader transmitToWebServer(StringBuilder requestParams)
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        if (WEB_SERVER_URL == null) {
            InputStream propFileStream = DataTransmissionHandler.class.getResourceAsStream("url.properties");
            Properties properties = new Properties();
            properties.load(propFileStream);


            WEB_SERVER_URL = properties.getProperty("web-server.url");

            // If it could not find the web-server url, loads the default one.
            if (WEB_SERVER_URL == null) {
                WEB_SERVER_URL = DEFAULT_WEB_SERVER_URL;
            }
        }

        URL url = new URL(WEB_SERVER_URL);
        URLConnection urlConn = null;

        if (WEB_SERVER_URL.startsWith("http")) {
            urlConn = url.openConnection();
        } else if (WEB_SERVER_URL.startsWith("https")) {
            /**
             * Usually, it is enough if you are sending request through HTTP protocol.
             * However, if you are sending a request to HTTPS domain, then the certificate
             * will be checked for validity. If program cannot find certification path, it
             * finds the certificate invalid. Now for our case TLS 1.0 certificate is not
             * registered which means the program cannot find the path and because of invalidity
             * of the certificate does not send a request. For this reason, it can be tricked as
             * follows:
             */
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(new KeyManager[0], new TrustManager[]{new DefaultTrustManager()}, new SecureRandom());
            SSLContext.setDefault(sslContext);

            HttpsURLConnection httpsUrlConn = (HttpsURLConnection) url.openConnection();
            httpsUrlConn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });

            urlConn = httpsUrlConn;
        }

        if (urlConn == null) {
            throw new IOException("Wrong URL was provided.");
        }

        urlConn.setUseCaches(false);

        // The request will return a response
        urlConn.setDoInput(true);

        // Setting request method to POST, in case of GET method the value will be false.
        urlConn.setDoOutput(true);

        // Sending data to the web-server
        OutputStreamWriter writer = new OutputStreamWriter(urlConn.getOutputStream());
        writer.write(requestParams.toString());
        writer.flush();

        // Reading response, store line by line in an array of Strings. It is has not been used yet!
        return new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
    }

    private static class DefaultTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
}
